/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.servicecomb.fence.edge;

import java.util.HashSet;
import java.util.Set;

import org.apache.servicecomb.fence.token.JWTToken;
import org.apache.servicecomb.fence.token.OpenIDToken;
import org.apache.servicecomb.fence.token.OpenIDTokenStore;
import org.apache.servicecomb.fence.util.CommonConstants;
import org.apache.servicecomb.foundation.common.utils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ServerWebExchange;


import com.huaweicloud.common.context.InvocationContext;
import com.huaweicloud.common.context.InvocationContextHolder;

import reactor.core.publisher.Mono;

public class AuthenticationEdgeFilter implements GlobalFilter, Ordered {

  @Autowired
  private OpenIDTokenStore openIDTokenStore;

  @Autowired
  private WebClient webClient;

  private static final Set<String> EXCLUDED_PATHS = new HashSet<>();

  static {
    // 初始化需要排除的路径
    EXCLUDED_PATHS.add("/v1/token");
    EXCLUDED_PATHS.add("/ui/admin");
//    EXCLUDED_PATHS.add("/api/admin");
  }

  public static boolean isPathExcluded(String path) {
    if (path == null || path.isEmpty()) {
      return false;
    }
    for (String excludedPath : EXCLUDED_PATHS) {
      if (path.contains(excludedPath)) {
        return true;
      }
    }

    return false;
  }

  @Override
  public int getOrder() {
    return -2000;
  }

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

    String path = exchange.getRequest().getURI().getPath();
    // 检查路径，如果是需要排除的路径，则直接跳过过滤逻辑
    if (isPathExcluded(path)) {
      return chain.filter(exchange); // 直接继续下一个过滤器
    }

    String authentication = exchange.getRequest().getHeaders().getFirst(CommonConstants.HTTP_HEADER_AUTHORIZATION);
    String type = exchange.getRequest().getHeaders().getFirst(CommonConstants.HTTP_HEADER_AUTHORIZATION_TYPE);
    InvocationContext context = exchange.getAttribute(InvocationContextHolder.ATTRIBUTE_KEY);

    if (authentication != null) {
      String[] tokens = authentication.split(" ");
      if (tokens.length == 2) {
        if (tokens[0].equals(CommonConstants.TOKEN_TYPE_BEARER)) {

          context.putContext(CommonConstants.CONTEXT_HEADER_AUTHORIZATION, tokens[1]);
          context.putContext(CommonConstants.CONTEXT_HEADER_AUTHORIZATION_TYPE,
              type == null ? CommonConstants.AUTHORIZATION_TYPE_ACCESS_TOKEN : type);
        }
      }
    }

    String token = context.getContext(CommonConstants.CONTEXT_HEADER_AUTHORIZATION);
    String tokenType = context.getContext(CommonConstants.CONTEXT_HEADER_AUTHORIZATION_TYPE);
    if (token == null) {

      exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
      return exchange.getResponse().setComplete();
    }

    if (CommonConstants.AUTHORIZATION_TYPE_ID_TOKEN.equals(tokenType)) {
      JWTToken jwtToken = openIDTokenStore.createIDTokenByValue(token);
      if (jwtToken == null || jwtToken.isExpired()) {

        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
      }

      // send id_token to services to apply state less validation
      exchange.getRequest().mutate()
          .header(CommonConstants.CONTEXT_HEADER_AUTHORIZATION, jwtToken.getValue())
          .build();
      return chain.filter(exchange);
    } else if (CommonConstants.AUTHORIZATION_TYPE_ACCESS_TOKEN.equals(tokenType)) {

      return webClient.post()
          .uri("/authentication/v1/token/query?accessToken="+token)
          .retrieve()
          .bodyToMono(OpenIDToken.class)
          .flatMap(openToken -> {
            exchange.getRequest().mutate()
                .header(CommonConstants.CONTEXT_HEADER_AUTHORIZATION, openToken.getIdToken().getValue())
                .build();
            return chain.filter(exchange);
          })
          .onErrorResume(e -> {
            // 错误处理逻辑
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
          });
    } else {

      exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
      return exchange.getResponse().setComplete();
    }
  }
}
